#include "ColladaExporter.h"
#include <maya/MGlobal.h>
#include <maya/MDistance.h>
#include <maya/MCommonSystemUtils.h>
#include <maya/MFileIO.h>
#include <maya/MVector.h>
#include <maya/MFnCamera.h>
#include <maya/MFnLight.h>
#include <maya/MFnSpotLight.h>
#include <maya/MFnPointLight.h>
#include <maya/MFnAmbientLight.h>
#include <maya/MFnDirectionalLight.h>
#include <maya/MItDag.h>
#include <maya/MFnDagNode.h>
#include <maya/MDagPath.h>
#include <list>

// Represents the exported scene.
class ExportedData
{
public:
	list<MFnCamera>		m_Cameras;		// The list of exported cameras.
	list<MFnLight>		m_Lights;		// The list of exported lights.
};

// Writes the <asset> element of the document.
void WriteDocumentAsset(ColladaCOLLADAWriter& collada, const MString& fileName, const ColladaExporterOptions& options, const MString& optionsString)
{
	// Open <asset>.
	auto asset = collada.OpenAsset();

	// Add contributor.
	auto contributor = asset.OpenContributor();
	contributor.AddAuthor(GetCurrentUserName());
	contributor.AddAuthoringTool((MString("Autodesk Maya ") + MGlobal::mayaVersion()).asChar());
	contributor.AddComments((MString("Exported with MayaColladaExporter " PLUGIN_VERSION " - options: ") + optionsString).asChar());
	contributor.AddSourceData(MFileIO::currentFile().asChar());
	contributor.Close();

	// Add creation date/time.
	asset.AddCreated(time(NULL));
				
	// Add keywords.
	if (options.m_Keywords.length() > 0) asset.AddKeywords(options.m_Keywords.asChar());

	// Add revision.
	if (options.m_Revision.length() > 0) asset.AddRevision(options.m_Revision.asChar());

	// Add subject.
	if (options.m_Subject.length() > 0) asset.AddSubject(options.m_Subject.asChar());

	// Add title.
	if (options.m_Title.length() > 0) asset.AddTitle(options.m_Title.asChar());

	// Add units info.
	MDistance::Unit unit = MDistance::uiUnit();
	if (unit != MDistance::kMeters)
	{
		double asMeters = MDistance(1.0, unit).asMeters();
		switch (unit)
		{
			// TODO: determine which phrasing is better: inches/feet (as the documentation suggests) or inch/foot like other units.
			case MDistance::kInches: asset.AddUnit("inches", asMeters); break;
			case MDistance::kFeet: asset.AddUnit("feet", asMeters); break;
			case MDistance::kYards: asset.AddUnit("yard", asMeters); break;
			case MDistance::kMiles: asset.AddUnit("mile", asMeters); break;
			case MDistance::kMillimeters: asset.AddUnit("millimeter", asMeters); break;
			case MDistance::kCentimeters: asset.AddUnit("centimeter", asMeters); break;
			case MDistance::kKilometers: asset.AddUnit("kilometer", asMeters); break;
			case MDistance::kMeters: asset.AddUnit("meter", asMeters); break;
		}
	}

	// Add axis info.
	MVector upAxis = MGlobal::upAxis();
	if (upAxis == MVector::xAxis) asset.AddUpAxis(ColladaAxis::X);
	else if (upAxis == MVector::yAxis) asset.AddUpAxis(ColladaAxis::Y);
	else if (upAxis == MVector::zAxis) asset.AddUpAxis(ColladaAxis::Z);
	
	// Close <asset>.
	asset.Close();
}

MStatus ColladaExporter::writer(const MFileObject& file, const MString& optionsString, FileAccessMode mode)
{
	try
	{
		// Get the full path.
		MString fileName = file.fullName();

		// Parse options.
		ColladaExporterOptions options(optionsString);

		// Build the list of objects to export.
		ExportedData exportedData;
		for (MItDag iterator; !iterator.isDone(); iterator.next())
		{
			MObject currentItem = iterator.currentItem();
			MFnDagNode dagNode(currentItem);
			MGlobal::displayInfo(MString("found ") + MString(currentItem.apiTypeStr()) + MString(" ") + dagNode.fullPathName());
			if (currentItem.hasFn(MFn::kLight)) exportedData.m_Lights.push_back(MFnLight(currentItem));
			if (currentItem.hasFn(MFn::kCamera)) exportedData.m_Cameras.push_back(MFnCamera(currentItem));
		}

		// Open COLLADA writer.
		ColladaFileWriter writer(ColladaVersion::Version150);
		if (!writer.SetPath(fileName.asChar()))
		{
			MGlobal::displayError(MString("Could not open file ") + fileName + MString(" for writing"));
			return MStatus::kFailure;
		}
		
		// Open <COLLADA>.
		auto COLLADA = writer.OpenCOLLADA();
		
		// Write <asset>.
		WriteDocumentAsset(COLLADA, fileName, options, optionsString);

		// Write cameras.
		auto library_cameras = COLLADA.OpenLibraryCameras();
		for each (auto camera in exportedData.m_Cameras)
		{
			camera.
		}
		library_cameras.Close();

		// Close <COLLADA>.
		COLLADA.Close();

		// Okay.
		return MStatus::kSuccess;
	}
	catch (ColladaExporterException e)
	{
		MGlobal::displayError(MString("An exception occurred in the MayaColladaExporter plug-in: ") + e.ToString());
		return MStatus::kFailure;
	}
	catch (...)
	{
		MGlobal::displayError("An unknown exception occurred in the MayaColladaExporter plug-in");
		return MStatus::kFailure;
	}
}

MPxFileTranslator::MFileKind ColladaExporter::identifyFile(const MFileObject& fileName, const char* buffer, short size) const
{
	const char* name = fileName.name().asChar();
	size_t nameLength = strlen(name);
	if ((nameLength > 5) && !strcasecmp(name + nameLength - 4, ".dae")) return kIsMyFileType;
	return kNotMyFileType;
}

ColladaExporter::ColladaExporter() : MPxFileTranslator() { }
ColladaExporter::~ColladaExporter() { }
void* ColladaExporter::creator() { return new ColladaExporter(); }
bool ColladaExporter::haveReadMethod() const { return false; }
bool ColladaExporter::haveWriteMethod() const { return true; }
MString ColladaExporter::defaultExtension() const { return MString("dae"); }
